class_name GameContainer
extends TileMapLayer

var container_width  := 12
var container_height := 22

@export var offset           := Vector2i(-((container_width -2)/2), -((container_height -2)/2))
const nextBrickOffset        := Vector2i(15, 5)
const currentBrickOffset     := Vector2i(5, 0)
const specialBrickOffset     := Vector2i(0, 1)
var currentShape: Global.Shapes
var nextShape: Global.Shapes
var currentBrick: Array
var landedBricks: Array
var autoFallAwait            := 1.0
var quickFallAwait           := 0.0
var moveAwait                := 0.0
const autoFallCooldown       := 0.5
const quickFallCooldown      := 0.1
const horizontalMoveCooldown := 0.06
var cells_to_clear: Array    =  []
var cells_to_draw: Array     =  []
const brick_texture_coords   := Vector2i(2, 1)
const border_texture_coords  := Vector2i(6, 1)


func _ready():
	draw_border()
	get_next_brick()
	spawn_current_brick()
	screen_refresh()


func _physics_process(delta):
	var fallDownSuccess := true
	autoFallAwait -= delta
	if autoFallAwait <= 0:
		autoFallAwait = autoFallCooldown
		fallDownSuccess = fallDown()

	# 如果按着“下”
	if Input.is_action_pressed("move_down"):
		quickFallAwait -= delta
		if quickFallAwait <= 0:
			quickFallAwait = quickFallCooldown
			fallDownSuccess =fallDown()

	# 无法下落 当前砖块转化成落地砖块
	if not fallDownSuccess:
		transform_to_land_bricks()
		checkAndClearFullLine(currentBrick)
		spawn_current_brick()
	else:
		if Input.is_action_just_pressed("rotate"):
			rotateBrick()

		if Input.is_action_just_pressed("move_left") and moveAwait <= 0:
			moveAwait = horizontalMoveCooldown
			horizontalMove(Vector2i.LEFT)
		elif Input.is_action_pressed("move_left"):
			moveAwait -= delta
			if moveAwait <= 0:
				moveAwait = horizontalMoveCooldown
				horizontalMove(Vector2i.LEFT)

		if Input.is_action_just_pressed("move_right") and moveAwait <= 0:
			moveAwait = horizontalMoveCooldown
			horizontalMove(Vector2i.RIGHT)
		elif Input.is_action_pressed("move_right"):
			moveAwait -= delta
			if moveAwait <= 0:
				moveAwait = horizontalMoveCooldown
				horizontalMove(Vector2i.RIGHT)

	checkGameOver()
	cells_to_draw.append_array(currentBrick)
	screen_refresh()


func draw_border():
	for i in  container_width:
		for j in container_height:
			if(i == 0 or i == container_width-1 or j == 0 or j ==container_height-1):
				set_cell(Vector2i(i-1, j-1) + offset, 0, border_texture_coords, 0)


func spawnNextBrick():
	pre_clear_next_brick()
	get_next_brick()
	pre_draw_next_brick()


func get_next_brick():
	nextShape = Global.Shapes.values().pick_random()
	while nextShape == currentShape:
		nextShape = Global.Shapes.values().pick_random()


func pre_draw_next_brick():
	for cell in Global.cells[nextShape]:
		cells_to_draw.append(cell + nextBrickOffset)


# 根据shape生成当前Brick
func spawn_current_brick():
	currentBrick.clear()
	currentShape = nextShape
	if(currentShape == Global.Shapes.T):
		for cell in Global.cells[currentShape]:
			currentBrick.append(cell + currentBrickOffset + specialBrickOffset)
	else:
		for cell in Global.cells[currentShape]:
			currentBrick.append(cell + currentBrickOffset)
	cells_to_draw.append_array(currentBrick)
	spawnNextBrick()


func pre_clear_next_brick():
	for cell in Global.cells[nextShape]:
		cells_to_clear.append(cell + nextBrickOffset)


func fallDown () -> bool:
	var currentCopy: Array = []
	for cell in currentBrick:
		currentCopy.append(cell+Vector2i.DOWN)
	if not checkLanded(currentCopy):
		cells_to_clear.append_array(currentBrick)
		currentBrick = currentCopy
		return true
	else:
		return false


func horizontalMove(vector: Vector2i):
	var currentCopy: Array = []
	for cell in currentBrick:
		currentCopy.append(cell+vector)
	if not checkCollision(currentCopy):
		cells_to_clear.append_array(currentBrick)
		currentBrick = currentCopy


func rotateBrick() -> void:
	if currentShape == Global.Shapes.O:
		return

	var rotate_offset: Vector2i  = currentBrick[0]
	var transform2D: Transform2D = Global.clockwise_rotation
	var currentCopy: Array       = []
	for i in currentBrick.size():
		var cell: Vector2i  = currentBrick[i]
		cell -= rotate_offset
		var coord: Vector2i = transform2D.basis_xform(cell)
		currentCopy.append(coord + rotate_offset)
	if not checkCollision(currentCopy):
		cells_to_clear.append_array(currentBrick)
		currentBrick = currentCopy


func checkCollision(brick: Array) -> bool:
	for cell in brick:
		if cell.x > 9 or cell.x < 0 or cell.y > 19:
			return true
		if landedBricks.rfind(cell) > -1:
			return true
	return false


func checkLanded(brick: Array) -> bool:
	for cell in brick:
		if cell.y > 19:
			return true
		if landedBricks.rfind(cell) > -1:
			return true
	return false


func transform_to_land_bricks():
	landedBricks.append_array(currentBrick)


func checkAndClearFullLine(cells: Array):
	# 当前落地砖块涉及到的行 因为只有这几行需要检查
	var verticals := cells.map(func (cell: Vector2i):return cell.y)
	# 统计检查每行的砖块数
	var count := {}
	for y in verticals:
		count[y] = 0

	for brick in landedBricks:
		if verticals.find(brick.y) > - 1:
			count[brick.y] += 1
	# 找出满了的行
	var fullLine: Array
	for key in count.keys():
		if count[key]>=10:
			fullLine.append(key)

	# 对于满了的每一行都需要  删除当前行   和  当前行之上的行下落一格
	for line in fullLine:
		# 删除当前行
		landedBricks = landedBricks.filter(func (cell: Vector2i):
			if cell.y == line:
				return false
			else:
				return true
		# 当前行之上的砖块下落一格
		).map(func (cell: Vector2i):
			if cell.y<line:
				cell.y += 1
			return cell
		)
	# 重绘已落地砖块
	if fullLine.size() > 0:
		reDrawAll(landedBricks)


func checkGameOver():
	for cell in currentBrick:
		if landedBricks.rfind(cell) > -1:
			currentBrick = []
			landedBricks=[]
			get_tree().reload_current_scene()


func screen_refresh():
	clearCells(cells_to_clear)
	cells_to_clear.clear()
	drawCells(cells_to_draw)
	cells_to_draw.clear()


func reDrawAll(cells: Array):
	# 清空整个面板
	for i in 10:
		for j in 20:
			set_cell(Vector2i(i, j) + offset, -1, Vector2i(-1, -1), -1)
	drawCells(cells)


func drawCells(cells: Array):
	for cell in cells:
		set_cell(cell + offset, 0, brick_texture_coords, 0)


func clearCells(cells: Array):
	for cell in cells:
		set_cell(cell + offset, -1, Vector2i(-1, -1), -1)
